/**
 * Created by udi on 2016/11/26.
 */
'use strict'
var {MongoClient, ObjectId ,ServerApiVersion} = require('mongodb') ;

let dbConfig = global.process.env;


let connectionUrl = `mongodb://${dbConfig.mongo_user}:${dbConfig.mongo_password}@${dbConfig.mongo_host}:${dbConfig.mongo_port}/${dbConfig.mongo_database}?ssl=false&authSource=admin&connectTimeoutMS=3000&authMechanism=SCRAM-SHA-1`;

logger.info("mongo数据库连接: " + connectionUrl)

let proc_where = (where) => {
	Object.keys(where).forEach(key => {
		var value = where[key];
		if ((key == "_id" || key.endsWith("._id")) && value  && value.length > 20 && ObjectId.isValid(value)) {
			where[key] = new  ObjectId(value);
		}
	});

	logger.info("where: " + JSON.stringify(where));
	return where;
};

let usingConnection = async (collectionName, callback) => {
	let client = new MongoClient(connectionUrl,{
		serverApi: {
			version: ServerApiVersion.v1,
			strict: true,
			deprecationErrors: true,
			authSource:"admin",
			connectTimeoutMS: 3000,
			directConnection: true,
			serverSelectionTimeoutMS:3000,
			ssl: false,
			authMechanism:"SCRAM-SHA-1"
		}
	});

    try {
        await client.connect();
    }
    catch(e){
        logger.error("无法连接mongo数据库: " + connectionUrl)
        throw e;
    }

	let collection = collectionName && client.db().collection(collectionName);
	logger.info("using " + collectionName);

	let data  = await callback(collection, client);

	client.close();

	return data;
};

let mongo_find = async (tab, where, skip, take) => {
	return await usingConnection(tab, async collection => {

			var query = collection.find(proc_where(where));
			if (skip) {
				query.skip(skip);
			}
			if (take) {
				query.limit(take);
			}

			if (skip || take) {
				logger.info("limit: " + skip + "," + take);
			}

			return query.toArray();

	});
};

let mongo_findOne = async (tab, where) => {
	let results = await mongo_find(tab, where, 0, 1);
	return results && results[0];
};

/**
 * for await ( var it of mor.readStreamEntity("tab",{} )){ }
 * @param tab
 * @param where
 * @param initSkip
 * @param batchSize
 * @returns {AsyncGenerator<*, void, ?>}
 */
async function* mongo_readStreamEntity(tab, where, initSkip, batchSize) {

	var skip = initSkip || 0;
	batchSize = batchSize || 25;
	var currentData = await mongo_find(tab, where, skip, batchSize);
	var current = 0;

	let next = async () => {
		if (currentData.length == 0) {
			return {done: true};
		}

		if (current.Between(0, currentData.length - 1)) {
			var ret = currentData[current];
			current++;
			return {done: false, value: ret};
		}

		if (currentData.size < batchSize) {
			return {done: true};
		}

		current = 0;

		skip += batchSize;

		currentData = await mongo_find(tab, where, skip, batchSize);
		if (currentData.length == 0) {
			return {done: true};
		}

		if (current.Between(0, currentData.length - 1)) {
			var ret = currentData[current];
			current++;
			return {done: false, value: ret};
		}

		return {done: true};
	};

	while (true) {
		var nextObj = await next();
		if (nextObj.done) break;
		yield nextObj.value;
	}
};

let mongo_update = async (tab, where, set) => {
	return await usingConnection(tab, async collection => {
		logger.info("update $set: " + JSON.stringify(set))
		var ret = await collection.updateMany(proc_where(where), {"$set": Object.assign({updateAt: new Date()},  set)}, true, true);

		if (ret.acknowledged) {
			logger.info("result: " + ret.matchedCount)
			return ret.matchedCount; // n是 where 条数， nModified:是影响行数
		}
		return null;
	});
}

let mongo_insert = async (tab, entity) => {
	if (!entity._id) {
		entity._id = ObjectId();
	}

    entity.createAt = new Date();

	return await usingConnection(tab, async collection => {
		logger.info("insert: " + JSON.stringify(entity))
		var ret = await  collection.insertOne(entity);

		if (ret.result.ok) {
			logger.info("result:" + ret.result.n);
			return ret.result.n; // n是 where 条数， nModified:是影响行数
		}
		return null;
	});
}

let mongo_remove = async (tab, where) => {
	return await usingConnection(tab, async collection => {
		logger.info("delete: ")
		let ret = await  collection.deleteMany(proc_where(where));

		if (ret.result.ok) {
			logger.info("result: " + ret.result.n)
			return ret.result.n; // n是 where 条数， nModified:是影响行数
		}
		return null;
	});
}


exports.proc_where = proc_where;
exports.get = usingConnection;
exports.readStreamEntity = mongo_readStreamEntity;
exports.find = mongo_find;
exports.findOne = mongo_findOne;
exports.update = mongo_update;
exports.insert = mongo_insert;
exports.remove = mongo_remove;

